package com.kly.sc.api.application.interceptors;


import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Response;
import com.fqgj.common.utils.StringUtils;
import com.kly.sc.api.application.annos.InitUser;
import com.kly.sc.api.application.annos.SignIgnore;
import com.kly.sc.api.application.annos.VisitorAccessible;
import com.kly.sc.api.application.config.UserRightsConfig;
import com.kly.sc.api.application.helper.RequestLocalInfo;
import com.kly.sc.api.application.utils.AppSignUtil;
import com.kly.sc.api.application.utils.JwtUtils;
import com.kly.sc.api.application.vo.UserAccessInfo;
import com.kly.sc.api.infras.app.RequestBasicInfo;
import com.kly.sc.api.infras.config.NacosConfigUtil;
import com.kly.sc.api.infras.enums.RestCodeEnum;
import com.kly.sc.api.infras.exception.ApplicationException;
import com.kly.user.constant.RedisKey;
import com.kly.user.dto.ExtendInfoDto;
import com.kly.user.dto.UserBaseDto;
import com.kly.user.enums.OnlineStatus;
import com.kly.user.enums.ResultStatus;
import com.kly.user.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: yujianfu (mr.vencnet@gmail.com)
 * Date: 2019-11-25
 * Time: 13:07
 */
@Component
public class AccessInterceptor implements HandlerInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccessInterceptor.class);

    public static final Set<String> ALLOWED_PATHS = new HashSet<>(Arrays.asList("/ok", "swagger-resources", "swagger-ui", "webjars"));

    @Resource
    private NacosConfigUtil nacosConfigUtil;
    @Resource
    private RedisClient redisClient;
    @Resource
    private UserService userService;
    @Resource
    UserRightsConfig userRightsConfig;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {

        String requestURI = request.getRequestURI();
        for (String s : ALLOWED_PATHS) {
            if (requestURI.equals(s)) {
                LOGGER.info("拦截器放行,url:{}", requestURI);
                return true;
            }
            if (requestURI.contains(s)) {
                LOGGER.info("拦截器放行,url:{}", requestURI);
                return true;
            }
        }

        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept,X-Requested-With,remember-me,bid,basicParams");
        RequestLocalInfo.clearThreadLocal();
        String ip = getClientIp(request);
        HandlerMethod handlerMethod = (HandlerMethod) o;
        SignIgnore signIgnoreAnnotation = handlerMethod.getMethodAnnotation(SignIgnore.class);
        if (signIgnoreAnnotation != null) {
            String basicParams = request.getHeader("basicParams");
            RequestBasicInfo basicVO = JSONUtil.toBean(basicParams, RequestBasicInfo.class);
            basicVO.setIp(ip);
            RequestLocalInfo.putCurrentRequestBasicVO(basicVO);
            return true;
        }

        verifySignInfo(request);
        RequestBasicInfo basicVO = RequestLocalInfo.getRequestBasicInfo();
        basicVO.setIp(ip);
        verifyToken(basicVO.getToken(), handlerMethod, request);

        return true;
    }

    private String getClientIp(HttpServletRequest request) {
        try {
            return ServletUtil.getClientIP(request);
        } catch (Exception e) {
            LOGGER.error("获取客户端IP地址异常"); ;
        }
        return StrUtil.EMPTY;
    }


    private void verifyToken(String accessToken, HandlerMethod handlerMethod, HttpServletRequest request) {
        //获取不要校验token的注解
        VisitorAccessible annotation = handlerMethod.getMethodAnnotation(VisitorAccessible.class);
        if (annotation != null && StringUtils.isEmpty(accessToken)) {
            return;
        }

        if (StringUtils.isEmpty(accessToken)) {
            throw new ApplicationException(RestCodeEnum.NEED_TOKEN_ERROR);
        }

        if (!hasPermission(accessToken, handlerMethod) && annotation == null) {
            LOGGER.error("User authentication failed");
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }
    }

    private void verifySignInfo(HttpServletRequest request) throws Exception {
        String basicParams = request.getHeader("basicParams");
        if (StringUtils.isEmpty(basicParams)) {
            throw new ApplicationException(RestCodeEnum.SYSTEM_PARAMETER_ERROR);
        }
        RequestBasicInfo basicVO = JSONUtil.toBean(basicParams, RequestBasicInfo.class);

        Map<String, Map<String, String>> appConfig = userRightsConfig.getAppConfig();
        Map<String, String> codeConfig = appConfig.get(basicVO.getAppCode());
        if (StringUtils.isNotEmpty(basicVO.getAppVersionName())) {
            if (basicVO.getAppVersionName().equalsIgnoreCase(codeConfig.get("appVersionName")) && basicVO.getAppSource().equals(codeConfig.get("appSource"))) {
                basicVO.setVersionControl(1);
            }
        }

        boolean isH5 = RequestBasicInfo.wechatUser(basicVO.getChannel());
        if (!isH5 && basicVO.getVersionCode() < 1 && !request.getRequestURI().contains("/home")) {
            throw new ApplicationException(RestCodeEnum.APP_VERSION_EXPIRED);
        }
        boolean needVerifyAppSign = !isH5 && !request.getRequestURI().contains("appcode/get") && !request.getRequestURI().contains("assistance/submit");

        if (needVerifyAppSign && !AppSignUtil.verifyAppSign(basicVO, "", nacosConfigUtil.isServerTest())) {
            throw new ApplicationException(RestCodeEnum.PARAMETER_SIGN_ERROR);
        }

        RequestLocalInfo.putCurrentEnvIsTest(nacosConfigUtil.isServerTest());
        RequestLocalInfo.putCurrentRequestBasicVO(basicVO);
    }


    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) throws Exception {

        try {
//            String url = httpServletRequest.getRequestURI();
//            Long startTime = Long.parseLong(MDC.get("startTime"));
//            Long costTime = System.currentTimeMillis() - startTime;

            //todo 增加访问历史日志记录
        } catch (Exception ex) {
            LOGGER.error("afterCompletion error:" + ex);
        }
    }

    public AccessInterceptor() {
    }

    private Boolean hasPermission(String accessToken, HandlerMethod handlerMethod) {

        if (!JwtUtils.validateToken(accessToken)) {
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }

        String userCode = JwtUtils.getAuthentication(accessToken).getPrincipal().toString();

        String redisToken = redisClient.get("key_{}_{}", userCode, RedisKey.TOKEN_CODE);
        if (!accessToken.equals(redisToken)) {
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }

        //获取用户信息
        UserBaseDto userByUserCode = userService.getUserBaseInfo(userCode).getData();
        if (userByUserCode == null) {
//            throw new ApplicationException(RestCodeEnum.USER_NOT_EXIST_ERROR);
            // 转到登陆页面重新注册
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }

        if (StringUtils.isNotEmpty(userByUserCode.getEmail())) {
            String booleanLoginBlack = redisClient.hGet(RedisKey.USER_BLACK, userByUserCode.getEmail());
            if (StringUtils.isNotEmpty(booleanLoginBlack)) {
                throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
            }
        }
        if (StringUtils.isNotEmpty(userByUserCode.getMobile())) {
            String booleanLoginBlack = redisClient.hGet(RedisKey.USER_BLACK, userByUserCode.getMobile());
            if (StringUtils.isNotEmpty(booleanLoginBlack)) {
                throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
            }
        }

        InitUser annotation = handlerMethod.getMethodAnnotation(InitUser.class);
        if ((!userByUserCode.getIsUserValid()) && annotation == null) {
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }
        String onlineKey = "meboo_online_statusV2_{}";
        Object onlineStatus = redisClient.get(onlineKey, userCode);
        if (Objects.isNull(onlineStatus)) {
            LOGGER.info("==meboo_online_statusV2 更新用户上线状态 发现未缓存在redis中，需要设置在缓存中==， userCode:{}", userCode);

            Integer result = userService.updateOnline(userCode);
            LOGGER.info("==meboo_online_statusV2 更新用户上线状态 更新完成==， userCode:{},结果:{}", userCode,result);

            redisClient.set(onlineKey, OnlineStatus.ONLINE_STATUS.getValue(), 30, userCode);
            LOGGER.info("==meboo_online_statusV2 更新用户上线状态 状态缓存到redis中==， userCode:{},结果:{}", userCode,result);
        } else {
            LOGGER.info("==meboo_online_statusV2 状态已经在redis中 不需要更新==， userCode:{}", userCode);
        }


//        if (redisClient.get(ApiRedisKeys.USER_ONLINE_STATUS.getValue(), userCode) == null) {
//            try {
//        userService.updateOnline(userCode);
//                redisService.addDelayTask(ApiRedisKeys.USER_ONLINE_QUEUE.getValue(),
//                        userCode, RememberTime.THREE_MINUTES.getTime(), "userOnlineQueue");
//                redisClient.set(ApiRedisKeys.USER_ONLINE_STATUS.getValue(),
//                        RememberTime.One_MINUTES.getCode(), RememberTime.One_MINUTES.getTime(), userCode);
//            } catch (Exception e) {
//                LOGGER.error("updateOnline error:" + e);
//            }
//
//        }
        //更新用户的最新在线时间，时间戳的方式
        UserAccessInfo userAccessInfo = new UserAccessInfo();
//        userAccessInfo.setAppsFlyerUid(userByUserCode.getAppsFlyerUid());
        String extendInfo = userByUserCode.getExtendInfo();
        if (StringUtils.isNotEmpty(extendInfo)) {
            ExtendInfoDto extendInfoDto = JSONUtil.toBean(extendInfo, ExtendInfoDto.class);
            userAccessInfo.setTurnOnsList(extendInfoDto.getTurnOnsList());
        }
        userAccessInfo.setUserCode(userCode);
        userAccessInfo.setMobile(userByUserCode.getMobile());
        userAccessInfo.setNewUserFlag(userByUserCode.getNewUserFlag());

        RequestLocalInfo.putCurrentUser(userAccessInfo);
        return true;
    }


}


